package com.cardone.common.util;

import com.google.common.collect.Maps;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializer;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.lang.reflect.Type;
import java.util.Date;
import java.util.Map;

/**
 * bean 工具
 *
 * @author yaohaitao
 */
@Slf4j
public class MapperUtils {
    private static Type mapType = new TypeToken<Map<String, Object>>() {
        /**
         *
         */
        private static final long serialVersionUID = -9096570133906255553L;
    }.getType();

    private static Map<String, Gson> gsonMap = com.google.common.collect.Maps.newConcurrentMap();

    private MapperUtils() {
    }

    /**
     * 获取bean关于json
     *
     * @param mappedClass 映射类型
     * @param jsonString
     * @return bean @
     */
    public static <T> T fromJson(final Class<T> mappedClass, final String jsonString) {
        if (StringUtils.isBlank(jsonString)) {
            return null;
        }

        return fromJson(TypeToken.of(mappedClass).getType(), jsonString);
    }

    /**
     * 获取bean关于json
     *
     * @param typeOfT    映射类型
     * @param jsonString
     * @return bean @
     */
    public static <T> T fromJson(Type typeOfT, final String jsonString) {
        if (StringUtils.isBlank(jsonString)) {
            return null;
        }

        return builderGson(null).fromJson(jsonString, typeOfT);
    }

    /**
     * 生成gson
     *
     * @param datePattern 日期结构
     * @return
     */
    @Synchronized
    private static Gson builderGson(final String datePattern) {
        String cacheKey = "datePattern:" + datePattern;

        Gson gson = gsonMap.get(cacheKey);

        if (gson != null) {
            return gson;
        }

        com.google.gson.GsonBuilder gsonBuilder = new com.google.gson.GsonBuilder().enableComplexMapKeySerialization().serializeNulls().setPrettyPrinting();

        gsonBuilder = gsonBuilder.registerTypeAdapter(String.class, (JsonSerializer<String>) (src, typeOfSrc, context) -> new JsonPrimitive(StringUtils.defaultString(src, StringUtils.EMPTY)));

        gsonBuilder = gsonBuilder.registerTypeAdapter(Date.class, (JsonDeserializer<Date>) (json, typeOfT, context) -> DateUtils.parseDate(json.getAsString()));

        gsonBuilder = gsonBuilder.registerTypeAdapter(Integer.class, (JsonDeserializer<Integer>) (json, typeOfT, context) -> NumberUtils.toInt(json.getAsString(), NumberUtils.INTEGER_ZERO));

        gsonBuilder = gsonBuilder.registerTypeAdapter(Double.class, (JsonDeserializer<Double>) (json, typeOfT, context) -> NumberUtils.toDouble(json.getAsString(), NumberUtils.DOUBLE_ZERO));

        gsonBuilder = gsonBuilder.registerTypeAdapter(Long.class, (JsonDeserializer<Long>) (json, typeOfT, context) -> NumberUtils.toLong(json.getAsString(), NumberUtils.LONG_ZERO));

        gsonBuilder = gsonBuilder.registerTypeAdapter(Float.class, (JsonDeserializer<Float>) (json, typeOfT, context) -> NumberUtils.toFloat(json.getAsString(), NumberUtils.FLOAT_ZERO));

        gsonBuilder = gsonBuilder.registerTypeAdapter(Short.class, (JsonDeserializer<Short>) (json, typeOfT, context) -> NumberUtils.toShort(json.getAsString(), NumberUtils.SHORT_ZERO));

        gsonBuilder = gsonBuilder.setDateFormat(com.cardone.common.util.StringUtils.defaultIfBlank(datePattern, com.cardone.common.util.DateUtils.DEFAULT_DATE_PATTERN));

        gson = gsonBuilder.create();

        gsonMap.put(cacheKey, gson);

        return gson;
    }

    /**
     * 获取bean关于json
     *
     * @param mappedClass 映射类型
     * @param paramName   参数名称
     * @param map
     * @return @
     */
    public static <T> T fromMapByParam(final Class<T> mappedClass, final String paramName, final Map<String, Object> map) {
        final Object paramJsonObject = MapUtils.getObject(map, paramName);

        if (paramJsonObject == null) {
            return null;
        }

        return MapperUtils.fromJsonByObject(mappedClass, paramJsonObject, null);
    }

    /**
     * 获取bean关于json
     *
     * @param mappedClass 映射类型
     * @param src
     * @return @
     */
    public static <T> T fromJsonByObject(final Class<T> mappedClass, final Object src) {
        return MapperUtils.fromJsonByObject(mappedClass, src, null);
    }

    /**
     * 获取bean关于json
     *
     * @param mappedClass 映射类型
     * @param src
     * @param datePattern 日期结构
     * @return @
     */
    public static <T> T fromJsonByObject(final Class<T> mappedClass, final Object src, final String datePattern) {
        final Gson gson = MapperUtils.builderGson(datePattern);

        final String srcJsonString = gson.toJson(src);

        return gson.fromJson(srcJsonString, TypeToken.of(mappedClass).getType());
    }

    /**
     * 获取bean关于json
     *
     * @param mappedClass 映射类型
     * @param paramName   参数名称
     * @param jsonString
     * @return bean @
     */
    public static <T> T fromJsonByParamName(final Class<T> mappedClass, final String paramName, final String jsonString) {
        final Object paramJsonObject = MapperUtils.fromJsonByParamNameForObject(paramName, jsonString);

        if (paramJsonObject == null) {
            return null;
        }

        return MapperUtils.fromJsonByObject(mappedClass, paramJsonObject);
    }

    /**
     * 获取对象
     *
     * @param paramName  参数名称
     * @param jsonString
     * @return 对象 @
     */
    public static Object fromJsonByParamNameForObject(final String paramName, final String jsonString) {
        final Map<String, Object> model = MapperUtils.fromJsonForMap(jsonString);

        return MapUtils.getObject(model, paramName);
    }

    /**
     * 获取键值对
     *
     * @param jsonString
     * @return 键值对 @
     */
    public static Map<String, Object> fromJsonForMap(final String jsonString) {
        if (StringUtils.isBlank(jsonString)) {
            return null;
        }

        try {
            return builderGson(null).fromJson(jsonString, MapperUtils.mapType);
        } catch (Exception e) {
            log.error(e.getMessage(), e);

            return Maps.newHashMap();
        }
    }

    /**
     * 获取bean关于json
     *
     * @param mappedType 映射类型
     * @param paramName  参数名称
     * @param map
     * @return @
     */
    public static <T> T fromMapByParam(final Type mappedType, final String paramName, final Map<String, Object> map) {
        return fromMapByParam(mappedType, paramName, map, null);
    }

    /**
     * 获取bean关于json
     *
     * @param mappedType 映射类型
     * @param src        参数名称
     * @return @
     */
    public static <T> T fromObject(final Type mappedType, final Object src) {
        return MapperUtils.fromObject(mappedType, src, null);
    }

    /**
     * 获取bean关于json
     *
     * @param mappedType  映射类型
     * @param src         参数名称
     * @param datePattern 日期结构
     * @return
     */
    public static <T> T fromObject(final Type mappedType, final Object src, final String datePattern) {
        final Gson gson = MapperUtils.builderGson(datePattern);

        final String srcJsonString = gson.toJson(src);

        return gson.fromJson(srcJsonString, mappedType);
    }

    /**
     * 获取bean关于json
     *
     * @param mappedType  映射类型
     * @param paramName   参数名称
     * @param map
     * @param datePattern
     * @return @
     */
    public static <T> T fromMapByParam(final Type mappedType, final String paramName, final Map<String, Object> map, String datePattern) {
        final Object paramJsonObject = MapUtils.getObject(map, paramName);

        if (paramJsonObject == null) {
            return null;
        }

        return MapperUtils.fromObject(mappedType, paramJsonObject, datePattern);
    }

    /**
     * 获取bean关于json
     *
     * @param mappedType 映射类型
     * @param paramName  参数名称
     * @param jsonString
     * @return bean @
     */
    public static <T> T fromJsonByParam(final Type mappedType, final String paramName, final String jsonString) {
        final Object paramJsonObject = MapperUtils.fromJsonByParamNameForObject(paramName, jsonString);

        if (paramJsonObject == null) {
            return null;
        }

        return MapperUtils.fromObject(mappedType, paramJsonObject);
    }

    /**
     * @param mappedClass
     * @param src
     * @return
     */
    public static <T> T fromObject(final Class<T> mappedClass, final Object src) {
        return fromObject(TypeToken.of(mappedClass).getType(), src, null);
    }

    /**
     * 转为json
     *
     * @param src object
     * @return string
     */
    public static String toJson(final Object src) {
        return builderGson(null).toJson(src);
    }

    /**
     * 转为json
     *
     * @param src         object
     * @param datePattern 日期结构
     * @return string
     */
    public static String toJson(final Object src, final String datePattern) {
        final Gson gson = MapperUtils.builderGson(datePattern);

        return gson.toJson(src);
    }
}
